﻿#ifndef ___PCLIB_S_SERIALPORT___
#define ___PCLIB_S_SERIALPORT___

#include "../m_core/m_core.hpp"
#include "../m_format/s_json.hpp"


/**
*@brief  串口连接对象
*/
class CPCSerialPort : CPCNoCopyable
{
public:
	CPCSerialPort()
		: m_Baudrate(9600)
		, m_Parity("N")
		, m_DataBits("8")
		, m_StopBits("1")
		, m_FlowControl("N")
	{
#if defined(PCOS_WIN)
		m_hDevice = NULL;
#else
		m_hDevice = -1;
#endif
    }
	~CPCSerialPort()
    {
		XClose();
    }

	/**
	*@brief     使用设置的属性连接串口
	*@param     pszPortName[in]		串口名，类似于 "COM1"  "/dev/ttys1" 
	*@param     pszPropsJson[in]    指定串口属性，可以只指定一部分，其他采用默认值或用SetProps设定。 Baudrate、Parity、FlowControl可以只填第一个字母
	*								Baudrate	: 110/300/600/1200/2400/4800/9600/14400/19200/38400/56000/57600/115200/921600
	*								Parity		: "No" "Odd" "Even" "Mark" "Space"
	*								DataBits	: "5" "6" "7" "8"
	*								StopBits	: "1" "1.5" "2"
	*								FlowControl	: "No" "Hardware" "Software"
	*@note		示例(这也是不设置时的默认值)：
	*							{"Baudrate":9600, "Parity":"N", "DataBits":"8", "StopBits":"1", "FlowControl":"N"}
	*							"{\"Baudrate\":9600, \"Parity\":\"N\", \"DataBits\":\"8\", \"StopBits\":\"1\",\"FlowControl\":\"N\"}"
	*@return    是否成功。
	*/
	CPCResult<> XConnect(const char* pszPortName, const char* pszPropsJson)
    {
		CPCResult<> result = SetProps(pszPropsJson);
		if (!result)
		{
			return result;
		}
		XClose();

#if defined(PCOS_WIN)
		//打开设备
		std::string strPortPath = std::string("\\\\.\\") + (pszPortName ? pszPortName : "");
		m_hDevice = CreateFileA(strPortPath.c_str(),
			GENERIC_READ | GENERIC_WRITE,
			0,//FILE_SHARE_READ | FILE_SHARE_WRITE, 
			NULL,
			OPEN_EXISTING,
			0,
			NULL);
		if (m_hDevice == INVALID_HANDLE_VALUE)
		{
			return result.SetSystemFail();
		}

		//获取属性
		DCB dcb;
		if (!::GetCommState(m_hDevice, &dcb))
		{
			return result.SetSystemFail();
		}

		//波特率
		COMMPROP comProps;
		::GetCommProperties(m_hDevice, &comProps);
		if (!(comProps.dwSettableBaud & m_Baudrate))
		{
			// 不支持该波特率
			return	result.SetFail("device not support this Baudrate from device : %", m_Baudrate);
		}
		switch (m_Baudrate) {
		case 150:		{	dcb.BaudRate = 150;		break;	}
		case 300:		{	dcb.BaudRate = 300;		break;	}
		case 600:		{	dcb.BaudRate = 600;		break;	}
		case 1200:		{	dcb.BaudRate = 1200;	break;	}
		case 2400:		{	dcb.BaudRate = 2400;	break;	}
		case 4800:		{	dcb.BaudRate = 4800;	break;	}
		case 9600:		{	dcb.BaudRate = 9600;	break;	}
		case 19200:		{	dcb.BaudRate = 19200;	break;	}
		case 38400:		{	dcb.BaudRate = 38400;	break;	}
		case 57600:		{	dcb.BaudRate = 57600;	break;	}
		case 115200:	{	dcb.BaudRate = 115200;	break;	}
		case 230400:	{	dcb.BaudRate = 230400;	break;	}
		case 460800:	{	dcb.BaudRate = 460800;	break;	}
		case 921600:	{	dcb.BaudRate = 921600;	break;	}
		default:
			return result.SetFail("device not support this Baudrate from list : %d ", m_Baudrate);
		}

		//校验位
		switch (m_Parity[0])
		{
		case 'N':
		case 'n':
			dcb.Parity = NOPARITY;
			break;
		case 'O':
		case 'o':
			dcb.Parity = ODDPARITY;
			break;
		case 'E':
		case 'e':
			dcb.Parity = EVENPARITY;
			break;
		case 'M':
		case 'm':
			dcb.Parity = MARKPARITY;
			break;
		case 'S':
		case 's':
			dcb.Parity = SPACEPARITY;
			break;
		default:
			return result.SetFail("device not support this Parity : %s ", m_Parity.c_str());
		}

		//数据位
		dcb.ByteSize = (BYTE)(m_DataBits[0] - 0x30);

		//停止位
		if (m_StopBits == "1")
		{
			dcb.StopBits = ONESTOPBIT;
		}
		else if(m_StopBits == "1.5")
		{
			dcb.StopBits = ONE5STOPBITS;
		}
		else if (m_StopBits == "2")
		{
			dcb.StopBits = TWOSTOPBITS;
		}
		else
		{
			return result.SetFail("device not support this StopBits : %s ", m_StopBits.c_str());
		}

		//流控制
		switch (m_FlowControl[0])
		{
		case 'N':
		case 'n':
			dcb.fOutxCtsFlow = FALSE;
			dcb.fRtsControl = RTS_CONTROL_DISABLE;
			dcb.fInX = FALSE;
			dcb.fOutX = FALSE;
			break;
		case 'H':
		case 'h':
			///< Hardware(RTS / CTS) flow control 硬件流控制
			dcb.fOutxCtsFlow = TRUE;
			dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
			dcb.fInX = FALSE;
			dcb.fOutX = FALSE;
			break;
		case 'S':
		case 's':
			///< Software(XON / XOFF) flow control 软件流控制
			dcb.fOutxCtsFlow = FALSE;
			dcb.fRtsControl = RTS_CONTROL_DISABLE;
			dcb.fInX = TRUE;
			dcb.fOutX = TRUE;
			break;
		default:
			return result.SetFail("device not support this FlowControl : %s ", m_FlowControl.c_str());
		}

		//OTHERS
		dcb.fDtrControl = DTR_CONTROL_DISABLE;

		//设置属性
		if (!::SetCommState(m_hDevice, &dcb))
		{
			return result.SetSystemFail();
		}
#else
		//打开设备
		std::string strPortName = (pszPortName ? pszPortName : "");
		m_hDevice = open(strPortName.c_str(),O_RDWR | O_NOCTTY | O_NONBLOCK);
		if (m_hDevice == -1)
		{
			return result.SetSystemFail();
		}
		
		//获取终端属性
		struct termios options;
		if (tcgetattr(m_hDevice, &options) < 0)
		{
			return result.SetSystemFail();
		}

		//波特率
		speed_t baudLinux = B38400;
		switch (m_Baudrate) {
		case 150:		{	baudLinux = B150	;	break;	}
		case 300:		{	baudLinux = B300	;	break;	}
		case 600:		{	baudLinux = B600	;	break;	}
		case 1200:		{	baudLinux = B1200	;	break;	}
		case 2400:		{	baudLinux = B2400	;	break;	}
		case 4800:		{	baudLinux = B4800	;	break;	}
		case 9600:		{	baudLinux = B9600	;	break;	}
		case 19200:		{	baudLinux = B19200	;	break;	}
		case 38400:		{	baudLinux = B38400	;	break;	}
		case 57600:		{	baudLinux = B57600	;	break;	}
		case 115200:	{	baudLinux = B115200 ;	break;	}
		case 230400:	{	baudLinux = B230400 ;	break;	}
		case 460800:	{	baudLinux = B460800 ;	break;	}
		case 921600:	{	baudLinux = B921600 ;	break;	}
		default:
			return result.SetFail("device not support this Baudrate : %d ", m_Baudrate);
		}
		cfsetospeed(&options, baudLinux);
		cfsetispeed(&options, baudLinux);

		//校验位
		switch (m_Parity[0])
		{
		case 'N':
		case 'n':
			options.c_cflag &= ~PARENB; // PARENB：产生奇偶位，执行奇偶校验
			options.c_cflag &= ~INPCK;  // INPCK：使奇偶校验起作用
			break;
		case 'O':
		case 'o':
			options.c_cflag |= PARENB; // PARENB：产生奇偶位，执行奇偶校验
			options.c_cflag |= PARODD; // PARODD：若设置则为奇校验,否则为偶校验
			options.c_cflag |= INPCK;  // INPCK：使奇偶校验起作用
			options.c_cflag |= ISTRIP; // ISTRIP：若设置则有效输入数字被剥离7个字节，否则保留全部8位
			break;
		case 'E':
		case 'e':
			options.c_cflag |= PARENB;  // PARENB：产生奇偶位，执行奇偶校验
			options.c_cflag &= ~PARODD; // PARODD：若设置则为奇校验,否则为偶校验
			options.c_cflag |= INPCK;   // INPCK：使奇偶校验起作用
			options.c_cflag |= ISTRIP; // ISTRIP：若设置则有效输入数字被剥离7个字节，否则保留全部8位
			break;
		case 'M':
		case 'm':
			options.c_cflag |= PARENB | CMSPAR | PARODD;
			break;
		case 'S':
		case 's':
			options.c_cflag &= ~PARENB; // PARENB：产生奇偶位，执行奇偶校验
			options.c_cflag &= ~CSTOPB; // CSTOPB：使用两位停止位
			break;
		default:
			return result.SetFail("device not support this Parity : %s ", m_Parity.c_str());
		}

		//数据位
		int ByteSize = (int)(m_DataBits[0] - 0x30);
		options.c_cflag &= ~CSIZE;		//屏蔽其它标志位
		switch (ByteSize)
		{
		case 5:
			options.c_cflag |= CS5;
			break;
		case 6:
			options.c_cflag |= CS6;
			break;
		case 7:
			options.c_cflag |= CS7;
			break;
		case 8:
			options.c_cflag |= CS8;
			break;
		default:
			return result.SetFail("device not support this DataBits : %s ", m_DataBits.c_str());
		}

		//停止位
		if (m_StopBits == "1")
		{
			// CSTOPB：使用两位停止位
			options.c_cflag &= ~CSTOPB; 
		}
		else if (m_StopBits == "2")
		{
			// CSTOPB：使用两位停止位
			options.c_cflag |= CSTOPB; 
		}
		else
		{
			return result.SetFail("device not support this StopBits : %s ", m_StopBits.c_str());
		}

		//流控制
		switch (m_FlowControl[0])
		{
		case 'N':
		case 'n':
			options.c_cflag &= ~CRTSCTS;
			break;
		case 'H':
		case 'h':
			///< Hardware(RTS / CTS) flow control 硬件流控制
			options.c_cflag |= CRTSCTS;
			break;
		case 'S':
		case 's':
			///< Software(XON / XOFF) flow control 软件流控制
			options.c_cflag |= IXON | IXOFF | IXANY;
			break;
		default:
			return result.SetFail("device not support this FlowControl : %s ", m_FlowControl.c_str());
		}

		//控制模式
		options.c_cflag |= CLOCAL; //保证程序不占用串口
		options.c_cflag |= CREAD;  //保证程序可以从串口中读取数据
		//设置输出模式为原始输出
		options.c_oflag &= ~OPOST; // OPOST：若设置则按定义的输出处理，否则所有c_oflag失效
		//设置本地模式为原始模式
		options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
		/*
		*ICANON：允许规范模式进行输入处理
		*ECHO：允许输入字符的本地回显
		*ECHOE：在接收EPASE时执行Backspace,Space,Backspace组合
		*ISIG：允许信号
		*/
		/*
		*BRKINT：如果设置了IGNBRK，BREAK键输入将被忽略
		*ICRNL：将输入的回车转化成换行（如果IGNCR未设置的情况下）(0x0d => 0x0a)
		*INPCK：允许输入奇偶校验
		*ISTRIP：去除字符的第8个比特
		*IXON：允许输出时对XON/XOFF流进行控制 (0x11 0x13)
		*/
		options.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
		//设置等待时间和最小接受字符
		options.c_cc[VTIME] = 0; //可以在select中设置
		options.c_cc[VMIN] = 1;  //最少读取一个字符
		//如果发生数据溢出，只接受数据，但是不进行读操作
		tcflush(m_hDevice, TCIFLUSH);

		//设置属性
		if (tcsetattr(m_hDevice, TCSANOW, &options) < 0)
		{
			return result.SetSystemFail();
		}
#endif
		return result;
    }
	
	/**
	*@brief     发送数据动作
	*@param     pdata[in]			数据
	*@param     length[in]			数据长度
	*@param     nTimeoutMs[in]		超时时间毫秒，<0代表永不超时
	*@return    是否成功
	*/
	CPCResult<> XSend(const void* pdata, size_t length, int nWaitMs = -1)
	{
		CPCResult<> result;
		if (pdata == NULL)
		{
			return result.SetFail("data invalid");
		}
		const char* data = (const char*)pdata;
#if defined(PCOS_WIN)
		if (m_hDevice == NULL || m_hDevice == INVALID_HANDLE_VALUE)
		{
			return result.SetFail("device not open");
		}
		COMMTIMEOUTS TimeOuts;
		if(!::GetCommTimeouts(m_hDevice, &TimeOuts))
		{
			return result.SetSystemFail();
		}
		TimeOuts.WriteTotalTimeoutConstant = nWaitMs > 0 ? nWaitMs : 0;
		if(!::SetCommTimeouts(m_hDevice, &TimeOuts))
		{
			return result.SetSystemFail();
		}

		DWORD dwSended = 0;
		CPCTimeValue tvCheck = CPCTimeValue::TickCount();
		while (1)
		{
			if (tvCheck.IsTimeOut(nWaitMs))
			{
				return result.SetFail("timeout:%d ms", nWaitMs);
			}
			DWORD dwNumberOfBytesWritten = 0;
			if (!::WriteFile(m_hDevice, data + dwSended, (DWORD)length - dwSended, &dwNumberOfBytesWritten, NULL))
			{
				return result.SetSystemFail();
			}
			if (dwNumberOfBytesWritten == 0)
			{
				break;
			}
			dwSended += dwNumberOfBytesWritten;
			if (dwSended >= length)
			{
				break;
			}
		}

		if (!::FlushFileBuffers(m_hDevice))
		{
			return result.SetSystemFail();
		}
#else
		if (m_hDevice == -1)
		{
			return result.SetFail("device not open");
		}

		CPCTimeValue tvStart = CPCTimeValue::TickCount();
		size_t nSendBytes = 0;
		while (1)
		{
			//可以主动发送0字节
			int nRetLen = (int)::write(m_hDevice, data + nSendBytes, (int)(length - nSendBytes));
			if (nRetLen > 0 && length > 0)
			{
				nSendBytes += nRetLen;

				//发完了
				if (nSendBytes >= length)
				{
					break;
				}
			}
			else if (nRetLen == 0 && length == 0)
			{
				//写入了0字节，只有主动发送0字节才会有这种情况。其他情况要报错
				break;
			}
			else
			{
				int nErrorCode = PCGetLastError();
				if (nErrorCode == EAGAIN)
				{
					//内核缓冲区写满了，稍等下，继续发
					PCSleep(100);
				}
				else
				{
					//出现错误，直接断开连接
					return result.SetSystemFail(nErrorCode);
				}
			}

			//超时了
			if (tvStart.IsTimeOut(nWaitMs))
			{
				return result.SetFail("send timeout.");
			}
		}
#endif
		return result;
	}


	/**
	*@brief     从接收数据流中读取数据，超时则直接关闭
	*@param     nWaitMs[in]			等待多少毫秒后超时。<0无限等待 0马上返回 >0为超时时间。
	*@param     nRecvSize[in]		要接收的数据长度
	*@param     bForceBufFull [in]	bForceBufFull = false   一般情况：超时/出错/用户缓冲区读满/有数据后间隔100毫秒无数据则返回
	*								bForceBufFull = true    例外情况：强制用户缓冲区读满则才返回，或者出现出错/超时。
	*@return    是否成功，成功后数据在CPCBuffer中
	*/
	CPCResult<CPCBuffer> XRecv(int nWaitMs, size_t nRecvSize, bool bForceBufFull = false)
	{
		CPCResult<CPCBuffer> result;
		if (nRecvSize == 0)
		{
			return result.SetFail("nRecvSize invalid");
		}
		auto& retBuffer = result.Get();
		retBuffer.reserve(nRecvSize);
#if defined(PCOS_WIN)
		if (m_hDevice == NULL || m_hDevice == INVALID_HANDLE_VALUE)
		{
			return result.SetFail("device not open");
		}
		COMSTAT comStat;
		DWORD errmask = 0;
		::ClearCommError(m_hDevice, &errmask, &comStat);

		COMMTIMEOUTS TimeOuts;
		if (!::GetCommTimeouts(m_hDevice, &TimeOuts))
		{
			return result.SetSystemFail();
		}
		TimeOuts.ReadIntervalTimeout = bForceBufFull ? 0 : 100;
		TimeOuts.ReadTotalTimeoutConstant = nWaitMs > 0 ? nWaitMs : 0;
		if (!::SetCommTimeouts(m_hDevice, &TimeOuts))
		{
			return result.SetSystemFail();
		}

		DWORD dwRecved = 0;
		CPCTimeValue tvCheck = CPCTimeValue::TickCount();
		while (1)
		{
			DWORD dwNumberOfBytesRead = 0;
			if (!::ReadFile(m_hDevice, retBuffer.data() + dwRecved, (DWORD)nRecvSize - dwRecved, &dwNumberOfBytesRead, NULL))
			{
				return result.SetSystemFail();
			}

			dwRecved += dwNumberOfBytesRead;
			retBuffer.addsize(dwNumberOfBytesRead);
			if (dwRecved >= nRecvSize)
			{
				break;
			}
			if (!bForceBufFull)
			{
				if (retBuffer.length() == 0)
				{
					return result.SetFail("not recved any data.");
				}
				break;
			}

			if (tvCheck.IsTimeOut(nWaitMs))
			{
				return result.SetFail("timeout:%d ms", nWaitMs);
			}
		}

		// 接收完成后清除缓冲区数据
		//::PurgeComm(m_hDevice, PURGE_TXCLEAR | PURGE_RXCLEAR);
#else
		if (m_hDevice == -1)
		{
			return result.SetFail("device not open");
		}
        nWaitMs = (nWaitMs == 0) ? 1 : nWaitMs;

        CPCTimeValue tvCheck = CPCTimeValue::TickCount();
        int nRecved = 0;
		while (1)
		{
            //wait
            CPCTimeValue tvNow = CPCTimeValue::TickCount();
            int leftTick = nWaitMs - (int)(tvNow - tvCheck);
            leftTick = (leftTick < 0) ? 0 : leftTick;
            if(nWaitMs > 0 && leftTick == 0)
            {
                return result.SetFail("stopwatch timeout");
            }
            bool readyToRead = false;
            bool readyToWrite = false;
            if (!WaitForReadOrWrite(&readyToRead, &readyToWrite, true, false,nWaitMs > 0 ? leftTick : -1))
            {
                return result.SetFail("WaitForReadOrWrite fail/timeout");;
            }
            if(!readyToRead)
            {
                return result.SetFail("readyToRead timeout.");
            }

			//读取
            int nBytesRead = read(m_hDevice, retBuffer.data() + nRecved, nRecvSize - nRecved );
			if (nBytesRead < 0)
            {
				int lastError = PCGetLastError();
				if(lastError != EAGAIN)
				{
					return result.SetSystemFail(lastError);
                }
			}
			else
            {
				nRecved += nBytesRead;
				retBuffer.addsize(nBytesRead);
				if (nRecved >= nRecvSize)
				{
					break;
				}
			}

			if (!bForceBufFull && retBuffer.length() > 0)
			{
				break;
            }
		}
#endif
		return result;
	}

	/**
	*@brief      主动执行关闭的动作
	*            执行这个动作会触发OnClosed事件
	*/
	void XClose()
	{
#if defined(PCOS_WIN)
		PCCloseHandle(m_hDevice);
#else
		if(m_hDevice != -1)
		{
			close(m_hDevice);
			m_hDevice = -1;
		}
#endif
	}

	/**
	*@brief	清理串口，这个函数在本类的其他成员函数中都不会主动调用，如果你需要清串口，一定要手动调用。
	*@param	bClearInputBuffer[in]		清理输入缓冲。
	*@param	bClearOutputBuffer[in]		清理输出缓冲。
	*/
    void XPurge(bool bClearInputBuffer = true, bool bClearOutputBuffer = true)
	{
#if defined(PCOS_WIN)
		if (m_hDevice != NULL && m_hDevice != INVALID_HANDLE_VALUE)
		{
			COMSTAT ComStat;
			DWORD   dwErrorFlags;
			::ClearCommError(m_hDevice, &dwErrorFlags, &ComStat);

			DWORD dwPurgeFlag = 0;
			if (bClearInputBuffer)
                dwPurgeFlag |= (PURGE_RXCLEAR | PURGE_RXABORT);
			if (bClearOutputBuffer)
                dwPurgeFlag |= (PURGE_TXCLEAR | PURGE_TXABORT);
			if (dwPurgeFlag != 0)
				::PurgeComm(m_hDevice, dwPurgeFlag);
		}
#else
		if (m_hDevice != -1)
		{
			if(bClearInputBuffer && bClearOutputBuffer)
			{
				ioctl(m_hDevice, TCFLSH, TCIOFLUSH);
				tcflush(m_hDevice, TCIOFLUSH);
			}
			else if (bClearInputBuffer && !bClearOutputBuffer)
			{
				ioctl(m_hDevice, TCFLSH, TCIFLUSH);
				tcflush(m_hDevice, TCIFLUSH);
			}
			else if (!bClearInputBuffer && bClearOutputBuffer)
			{
				ioctl(m_hDevice, TCFLSH, TCOFLUSH);
				tcflush(m_hDevice, TCOFLUSH);
			}
		}
#endif
	}

public:
	/**
	*@brief     枚举串口设备
	*@return    是否成功。map.first为串口名；map.second为串口描述。
	*/
	static CPCResult<std::unordered_map<std::string,std::string> > EnumDevice()
	{
		CPCResult<std::unordered_map<std::string, std::string> > result;
		auto& umDevices = result.Get();

#if defined(PCOS_WIN)
		//初始化环境
        // The GUID_DEVINTERFACE_COMPORT device interface class is defined for COM ports. GUID
        // {86E0D1E0-8089-11D0-9CE4-08003E301F73}
        //DEFINE_GUID(PC_GUID_DEVINTERFACE_COMPORT, 0x86E0D1E0L, 0x8089, 0x11D0, 0x9C, 0xE4, 0x08, 0x00, 0x3E, 0x30, 0x1F, 0x73);
        GUID PC_GUID_DEVINTERFACE_COMPORT = { 0x86E0D1E0L, 0x8089, 0x11D0, { 0x9C, 0xE4, 0x08, 0x00, 0x3E, 0x30, 0x1F, 0x73 } };
		HDEVINFO hDevInfo = SetupDiGetClassDevsA(&PC_GUID_DEVINTERFACE_COMPORT, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
		if (INVALID_HANDLE_VALUE == hDevInfo)
		{
			return result.SetSystemFail();
		}
		PC_DEFER([&hDevInfo](){ SetupDiDestroyDeviceInfoList(hDevInfo); });
		
		//枚举串口设备
		DWORD dwDetDataSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A)+MAX_PATH;
		SP_DEVICE_INTERFACE_DETAIL_DATA_A *pDetData = (SP_DEVICE_INTERFACE_DETAIL_DATA_A *)new char[dwDetDataSize];
		pDetData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A);
		PC_DEFER([&pDetData](){ PCDeleteArr(pDetData); });

		SP_DEVICE_INTERFACE_DATA did = { 0 };
		did.cbSize = sizeof(did);
		BOOL bQueryedSucc = TRUE;
		for (DWORD idev = 0; bQueryedSucc; idev++)
		{
			// enumerates the device interfaces that are contained in a device information set
			bQueryedSucc = SetupDiEnumDeviceInterfaces(hDevInfo, NULL, &PC_GUID_DEVINTERFACE_COMPORT, idev, &did);
			if (!bQueryedSucc)
			{
				int lastError = PCGetLastError();
				if (ERROR_NO_MORE_ITEMS == lastError)
				{
					break;
				}
				return result.SetSystemFail(lastError);
			}
			
			// DWORD cbRequired = 0;
			SP_DEVINFO_DATA devdata = { sizeof(SP_DEVINFO_DATA) };
			bQueryedSucc = SetupDiGetDeviceInterfaceDetailA(hDevInfo, &did, pDetData, dwDetDataSize, NULL, &devdata);
			if (!bQueryedSucc)
			{
				continue;
			}
			
			// retrieves a specified Plug and Play device property (include friendlyname etc.)
			char friendlyName[MAX_PATH] = { 0 };
			BOOL bSuccess = SetupDiGetDeviceRegistryPropertyA(hDevInfo, &devdata, SPDRP_FRIENDLYNAME, NULL, (PBYTE)friendlyName, sizeof(friendlyName)-1, NULL);

			// get port name
			char portName[MAX_PATH];
			HKEY hDevKey = SetupDiOpenDevRegKey(hDevInfo, &devdata, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ);
			if (INVALID_HANDLE_VALUE != hDevKey)
			{
				DWORD dwCount = sizeof(portName) - 1; // DEV_NAME_MAX_LEN
				auto nRet = RegQueryValueExA(hDevKey, "PortName", NULL, NULL, (BYTE *)portName, &dwCount);
				RegCloseKey(hDevKey);
				if (nRet == ERROR_SUCCESS)
				{
					umDevices.insert(std::make_pair(portName, friendlyName));
				}
			}
		}
#elif defined(PCOS_MAC)
		return result.SetFail("not supported.");
#else
        // 1.Scan through /sys/class/tty - it contains all tty-devices in the system
        CPCFileUtil::PCScanDir("/sys/class/tty/", [&umDevices](const char* pszPath, const char* pszFileName, bool bDir)->CPCResult<>
        {
            // sys/class/tty/ttyS0/device
            std::string devicedir = std::string(pszPath) + "/device";

            // Stat the devicedir and handle it if it is a symlink
            struct stat st;
            std::string driver;
            if (lstat(devicedir.c_str(), &st) == 0 && S_ISLNK(st.st_mode))
            {
                // Append '/driver' and return basename of the target
                // VCs, ptys, etc don't have /sys/class/tty/<tty>/device/driver's
                // /sys/class/tty/ttyS0/device/driver
                devicedir += "/driver";
                char buffer[1024] = {0};
                if (readlink(devicedir.c_str(), buffer, sizeof(buffer)) > 0)
                {
                    driver = CPCFileUtil::PCBaseName(buffer);
                }
            }

            // Skip devices without a driver
            std::string devfile = std::string("/dev/") + pszFileName;
            if (driver == "serial8250")
            {
                // serial8250-devices must be probe to check for validity
                struct serial_struct serinfo;
                int fd = open(devfile.c_str(), O_RDWR | O_NONBLOCK | O_NOCTTY);
                if (fd >= 0)
                {
                    // Get serial_info
                    if (ioctl(fd, TIOCGSERIAL, &serinfo) == 0)
                    {
                        // If device type is no PORT_UNKNOWN we accept the port
                        if (serinfo.type != PORT_UNKNOWN)
                        {
                            umDevices.insert(std::make_pair(devfile, driver));
                        }
                    }
                    close(fd);
                }
            }
            else if(!driver.empty())
            {
                // Only non-serial8250 has been added to comList without any further testing
                umDevices.insert(std::make_pair(devfile, driver));
            }

            return CPCResult<>();
        });

        // 2.Scan through /dev/pts/- it contains all pseudo terminal(such as telnet, ssh etc.) in the system
        CPCFileUtil::PCScanDir("/dev/pts/", [&umDevices](const char* pszPath, const char* pszFileName, bool bDir)->CPCResult<>{
            CPCResult<> ret;
            if(!bDir && strcmp(pszFileName, "ptmx" ) != 0)
            {
                umDevices.insert(std::make_pair(pszPath, pszPath));
            }
            return ret;
        });
#endif
		return result;
	}

public:
	/**
	*@brief     设置的串口属性，可以只修改一部分。修改属性后，重新XConnect后才会生效
	*@param     pszPropsJson        [in]    指定串口属性，如果为空，则不改变已有属性。
	*@return    是否成功。任意值错误都不会修改已有设置。
	*/
	CPCResult<> SetProps(const char* pszPropsJson)
	{
		CPCResult<> result;
		if (pszPropsJson == NULL || pszPropsJson[0] == 0)
		{
			return result;
		}

		//解析
		CPCJsonObject jsonObj;
		result = jsonObj.Parse(pszPropsJson);
		if (!result)
		{
			return result;
		}
		m_Baudrate = jsonObj.Get("/Baudrate", 38400);
		m_Parity = jsonObj.Get("/Parity", "N");
		m_DataBits = jsonObj.Get("/DataBits", "8");
		m_StopBits = jsonObj.Get("/StopBits", "1");
		m_FlowControl = jsonObj.Get("/FlowControl", "N");
		return result;
	}

	/**
	*@brief     获取的串口属性
	*@return    串口属性JSON。
	*/
	std::string GetProps()
	{
		CPCJsonObject jsonObj;
		jsonObj.Set("/Baudrate", m_Baudrate);
		jsonObj.Set("/Parity", m_Parity.c_str());
		jsonObj.Set("/DataBits", m_DataBits.c_str());
		jsonObj.Set("/StopBits", m_StopBits.c_str());
		jsonObj.Set("/FlowControl", m_FlowControl.c_str());
		auto result = jsonObj.Stringify(true);
		return result.Get();
	}

protected:
#if defined(PCOS_WIN)
#else
    bool WaitForReadOrWrite(bool *selectForRead, bool *selectForWrite,bool checkRead, bool checkWrite,int msecs)
    {
        fd_set fdread;
        FD_ZERO(&fdread);
        if (checkRead)
            FD_SET(m_hDevice, &fdread);

        fd_set fdwrite;
        FD_ZERO(&fdwrite);
        if (checkWrite)
            FD_SET(m_hDevice, &fdwrite);

        struct timeval tv;
        tv.tv_sec = msecs / 1000;
        tv.tv_usec = (msecs % 1000) * 1000;

        const int ret = ::select(m_hDevice + 1, &fdread, &fdwrite, 0, msecs < 0 ? 0 : &tv);
        if (ret < 0) {
            //system error
            return false;
        }
        if (ret == 0) {
            //timeout
            return false;
        }

        *selectForRead = FD_ISSET(m_hDevice, &fdread);
        *selectForWrite = FD_ISSET(m_hDevice, &fdwrite);
        return true;
    }
#endif

protected:
	int			m_Baudrate;
	std::string m_Parity;
	std::string	m_DataBits;
	std::string	m_StopBits;
	std::string m_FlowControl;

protected:
#if defined(PCOS_WIN)
	HANDLE m_hDevice;
#else
	int    m_hDevice;
#endif
};



#endif    //___PCLIB_S_SERIALPORT___
